#include "Ball.h"
#include "BallSketcher.h"
#include "GeometryHelper.h"
#include <math.h>
#define REVERSAL_ACELERATION 1
Ball::Ball(){
	this->initialTime=0;
	this->nextCollision = NULL;
	this->nextRailIntersection = NULL;
	this->initialPosition= NULL ;
	this->maxPosition= NULL ;
	this->position = NULL;
}

Ball::Ball(Position* position){
	this->position = position;
	this->velocity = new Velocity(5,3);
	this->initialTime=0;
	this->nextCollision = NULL;
	this->nextRailIntersection = NULL;
	this->initialPosition= NULL ;
	this->maxPosition= NULL ;
}

Ball::Ball(Position* position,float radius){
	this->position = position;
	this->velocity = new Velocity(5,3);
	this->initialTime = 0;
	this->radius = radius;
	this->nextCollision = NULL;
	this->initialPosition = NULL;
	this->nextRailIntersection = NULL;
	this->initialPosition= NULL ;
	this->maxPosition= NULL ;
}
bool Ball::sameTypeThan(Ball* ball){

	return strcmp(this->getTexture()->getPath().c_str(),ball->getTexture()->getPath().c_str())==0;
}
float Ball::getRemainingTime(){
	if(reverseMode||stageReversePower){
		return initialTime;
	}else{
		return maxTime - initialTime;
	}
}
bool Ball::isStopped(){
	return stopped;
}

bool Ball::needToCheck(){
	return needCheck;
}

void Ball::setNeedCheck(bool needCheck){
	this->needCheck =needCheck;
}

void Ball::stop(){
	stopped=true;
}

void Ball::start(){
	stopped=false;
}

float Ball::calculateTimeToThisPosition(Position *p){
	float d = GeometryHelper::getInstance()->calculateDistance(this->initialPosition, p);
	return d/this->getVelocity()->getModule();
}

float Ball::calculateTime(Position * p){
	float d = GeometryHelper::getInstance()->calculateDistance(this->position, p);
	return d/this->getVelocity()->getModule();
}

Ball::Ball(Position* position, Velocity* velocity){
	this->velocity = velocity;
	this->position = position;
	this->initialTime=0;
	this->nextCollision = NULL;
	this->nextRailIntersection = NULL;
	this->initialPosition= NULL ;
	this->maxPosition= NULL ;
}

Ball::Ball(Color *color,Color *lineColor, Velocity *velocity){
	this->elementColor = color;
	this->lineColor = lineColor;
	this->velocity = velocity;
	this->initialTime=0;
	this->nextCollision = NULL;
	this->nextRailIntersection = NULL;
	this->position = NULL;
	this->initialPosition= NULL ;
	this->maxPosition= NULL ;
	this->position= NULL ;
}


void Ball::moveWithoutChekingObstacles(float time){
	if(stopped&&!this->stageReversePower) return;
	float xPosition = this->getInitialPosition()->getPixelX();
	float yPosition = this->getInitialPosition()->getPixelY();

	float xVelocity = this->getVelocity()->getXComponent();
	float yVelocity = this->getVelocity()->getYComponent();

	delete this->position;
	float actualTime;
	if(reverseMode||stageReversePower){
		actualTime = initialTime-REVERSAL_ACELERATION*time;
		this->initialTime=this->initialTime-REVERSAL_ACELERATION*time;
	}else{
		actualTime= initialTime+time;
		this->initialTime=this->initialTime+time;
	}
	this->setPosition(new Position(xPosition + xVelocity*actualTime, yPosition + yVelocity*actualTime));
	
}

Ball::~Ball() {
	delete this->position;
	if( this->maxPosition)
		delete this->maxPosition;
	if( this->initialPosition)
		delete this->initialPosition;
	delete this->velocity;
	if(nextCollision != NULL) {
		delete nextCollision;
	}

	if(nextRailIntersection != NULL) {
		delete nextRailIntersection;
	}

}

/*-------------Getters & Setters--------------------*/
float Ball::getNextTramo(){
	if(reverseMode||stageReversePower){
		this->tramo=this->tramo-1;
	}else{
		this->tramo=this->tramo+1;
	}
	return this->tramo;
}
void Ball::setReverseModeOff(){
	this->reverseMode=false;
}

void Ball::setReverseModeOn(){
	this->stopped=false;
	this->reverseMode=true;
}
void Ball::setStageReversePowerOff(){
	this->stageReversePower=false;
}

void Ball::setStageReversePowerOn(){
	this->stageReversePower=true;
}
SDL_Surface * Ball::getSurface(){
	return this->surface;
}

void Ball::setSurface(SDL_Surface * surface){
	this->surface=surface;
}
void Ball::setInitialPosition(Position *initialPosition){
	if(this->initialPosition != NULL){
		delete this->initialPosition ;
		this->initialPosition= NULL ;
	}
	this->initialTime = 0;
	this->maxTime = 0;
	this->initialPosition = initialPosition;
}

Position *Ball::getInitialPosition(){
	return this->initialPosition;
}

bool Ball::isInReverseMode(){
	return this->reverseMode;
}
void Ball::setMaxPosition(Position *maxPosition){
	if(this->maxPosition != NULL){
		delete this->maxPosition ;
		this->maxPosition= NULL ;
	}
	this->maxPosition = maxPosition;
	if(maxPosition==NULL){
		maxTime=0;
		return;
	}
	this->maxTime=calculateTimeToThisPosition(maxPosition);
}

void Ball::resetTime() {
	this->maxTime = 0;
	if(maxPosition != NULL && initialPosition != NULL) {
		this->maxTime=calculateTimeToThisPosition(maxPosition);
	}
	if(reverseMode||stageReversePower){
		this->initialTime = this->maxTime;
	}else{
		this->initialTime = 0;
	}	
}

bool Ball::hasPassedMaxPosition(float time){
	
	if(reverseMode||stageReversePower){
		if(this->initialTime-REVERSAL_ACELERATION*time<0){
			return true;
		}
	}else{
		if(this->maxTime==0) return false;
		if(time+this->initialTime>maxTime){
			return true;
		}
	}
	return false;

}
Position *Ball::getMaxPosition(){
	return this->maxPosition;
}
void Ball::setPosition(Position *position){
	this->position = position;
}

Position *Ball::getPosition(){
	return this->position;
}

void Ball::setVelocity(Velocity * velocity){
	this->velocity = velocity;
}

Velocity* Ball::getVelocity() {
	return this->velocity;
}

Color * Ball::getElementColor(){
	return this->elementColor;
}

void Ball::setElementColor(Color * color){
	this->elementColor = color;
}

float Ball::getRadius(){
	return this->radius;
}

void Ball::setRadius(float radius){
	this->radius = radius;
}

Texture *Ball::getTexture(){
	return this->texture;
}

void Ball::setTexture(Texture *texture){
	this->texture = texture;
}

Color * Ball::getLineColor(){
	return this->lineColor;
}

void Ball::setLineColor( Color * color ) {
	this->lineColor = color;
}

float Ball::getMinXPos(){
	return this->getPosition()->getAxisX() - this->radius;
}

float Ball::getMinYPos(){	
	return this->getPosition()->getAxisY() - this->radius;
}

float Ball::getMaxXPos(){	
	return this->getPosition()->getAxisX() + this->radius;
}

float Ball::getMaxYPos(){
	return this->getPosition()->getAxisY() + this->radius;
}


void Ball::evaluateNextCollision( Collision * collision )
{
	if(this->nextCollision == NULL) {
		this->setNextCollision(collision);
	} else {
		if(collision != NULL) {
			if(collision->getDistance() < this->nextCollision->getDistance()) {
				this->setNextCollision(collision);
			}
		}
	}
}

void Ball::setNextCollision( Collision * collision ) {
	setCollisionTime(collision);
	this->nextCollision = collision;
}

Collision * Ball::getNextCollision(){
	return this->nextCollision;
}

void Ball::draw(){
	/*Sketcher * sketcher = new BallSketcher(this);
	sketcher->draw();
	delete(sketcher);*/
	
	Position * ballPosition = this->getPosition();
	float ballRadius = this->getRadius();
	SketcherHelper::getInstance()->drawCircleFromSurface(ballPosition,ballRadius,this->getSurface());
}

Position * Ball::getNewPosition(float time){
	float xPosition = this->getInitialPosition()->getPixelX();
	float yPosition = this->getInitialPosition()->getPixelY();
	float xVelocity = this->getVelocity()->getXComponent();
	float yVelocity = this->getVelocity()->getYComponent();
	Position * newPosition = new Position(xPosition + xVelocity*(initialTime+time), yPosition + yVelocity*(initialTime+time));
	return newPosition;
}

void Ball::move(float time){
	bool railIntersection = false;
	bool col = false;
	if(this->nextCollision != NULL) {
		Position * collisionPoint = this->nextCollision->getPoint();
		if(this->nextCollision->getCollisionTime() <= 0) {
			if(this->nextCollision->getElement()->getType().compare(RAIL) == 0) {
				throw BallRailIntersectionException();
			}
			float compX = pow(this->getVelocity()->getXComponent(),2);
			float compY = pow(this->getVelocity()->getYComponent(),2);
			float module = pow( (compX+compY) ,0.5f);
			this->getVelocity()->setXComponent(module*this->nextCollision->getDeltaX());
			this->getVelocity()->setYComponent(module*this->nextCollision->getDeltaY());
			this->setInitialPosition(collisionPoint);
			this->setPosition(collisionPoint);
			this->initialTime = 0;
			col = true;
		}else {
			this->nextCollision->setCollisionTime(this->nextCollision->getCollisionTime()-time);
		}
	}
	this->setPosition(getNewPosition(time));	
	this->initialTime += time;

	if(col) {
		Element * element = this->getNextCollision()->getElement();
		delete this->nextCollision;
		this->setNextCollision(NULL);
		throw BallCollisionException("Colision de la bola." , element);
	}

}

void Ball::evaluateNextRailIntersection( Collision * collision ){
	if(this->nextRailIntersection == NULL) {
		this->setNextRailIntersection(collision);
	} else {
		if(collision != NULL) {
			if(collision->getDistance() < this->nextRailIntersection->getDistance()) {
				this->setNextRailIntersection(collision);
			}
		}
	}
}

Collision * Ball::getNextRailIntersection(){
	return this->nextRailIntersection;
}

void Ball::setNextRailIntersection(Collision * collision){
	setCollisionTime(collision);
	this->nextRailIntersection = collision;
}

bool Ball::hasImpactSense( int xPoint, int yPoint ){
	int dxSignum = xPoint-getPosition()->getPixelX()>=0 ? 1:-1;
	int vxSignum = getVelocity()->getXComponent()>=0 ? 1:-1;

	int dySignum = yPoint-getPosition()->getPixelY()>=0 ? 1:-1;
	int vySignum = getVelocity()->getYComponent()>=0 ? 1:-1;

	if((xPoint == getPosition()->getPixelX()) && (yPoint == getPosition()->getPixelY())) {
		return false;
	}
	if(
		((xPoint != getPosition()->getPixelX() && dxSignum == vxSignum)||(xPoint == getPosition()->getPixelX()))
	&&  ((yPoint != getPosition()->getPixelY() && dySignum == vySignum)||(yPoint == getPosition()->getPixelY()))
	){
			return true;
	}
	return false;
}

bool Ball::hasImpactSense( Position * point ){
	return this->hasImpactSense(point->getPixelX(), point->getPixelY());
}

Position * Ball::calculatePosition( float distance )
{
	float vx = this->getVelocity()->getXComponent();
	float vy = this->getVelocity()->getYComponent();

	float vx2 = pow(vx,2);
	float vy2 = pow(vy,2);
	float v = pow(vx2 + vy2, 0.5f);

	Position * pos = new Position();
	pos->setPixelX(this->position->getPixelX()+distance*vx/v);
	pos->setPixelY(this->position->getPixelY()+distance*vy/v);

	return pos;
}

float Ball::calculateDistance( Position * point ){
	return GeometryHelper::getInstance()->calculateDistance(getPosition(),point);
}

void Ball::setCollisionTime( Collision * collision ){
	if(collision != NULL) {
		float vx = getVelocity()->getXComponent();
		float vy = getVelocity()->getYComponent();
		float vx2 = pow(vx,2);
		float vy2 = pow(vy,2);
		float v = pow( (vx2+vy2),0.5f);

		collision->setCollisionTime(collision->getDistance()/v);
	}
}

bool Ball::compareTo( Ball * otherBall )
{
	return ((this->getTexture() != NULL && otherBall->getTexture() != NULL
			&& this->getTexture()->getId() == otherBall->getTexture()->getId())
			|| (this->getElementColor() != NULL && otherBall->getElementColor() != NULL
				&& this->getElementColor()->getRGB() == otherBall->getElementColor()->getRGB()));

	
}

void Ball::setIntersectionPosition( Position * intersectionPoint ){
	this->intersectionPosition = intersectionPoint;
}

Position* Ball::getIntersectionPosition( ){
	return this->intersectionPosition;
}

float Ball::getAngle(int signum){
	return GeometryHelper::getInstance()->getAngle(this->getM(),signum);
}

float Ball::getM( void ){
	float vx = getVelocity()->getXComponent();
	float vy = getVelocity()->getYComponent();
	float m = (vy / vx);
	return m;
}

float Ball::getTime()
{
	return this->initialTime;
}

void Ball::setTime( float time )
{
	this->initialTime = time;
}

float Ball::getMaxTime()
{
	return this->maxTime;
}

void Ball::setMaxTime( float time )
{
	this->maxTime = time;
}
